﻿//
// XN4SL - Xna Framework for Silverlight.
//
using System;
using System.ComponentModel;
using System.Windows;

namespace Xn4Sl.Framework
{
    /// <summary>
    /// Defines a rectangle.
    /// </summary>
    public struct Rectangle : IEquatable<Rectangle>
    {
        /// <summary>
        /// Specifies the height of the rectangle.
        /// </summary>
        public int Height;
        
        /// <summary>
        /// Specifies the width of the rectangle.
        /// </summary>
        public int Width;
        
        /// <summary>
        /// Specifies the x-coordinate of the rectangle.
        /// </summary>
        public int X;
        
        /// <summary>
        /// Specifies the y-coordinate of the rectangle.
        /// </summary>
        public int Y;
        
        /// <summary>
        /// Initializes a new instance of Rectangle.
        /// </summary>
        /// <param name="x">The x-coordinate of the rectangle.</param>
        /// <param name="y">The y-coordinate of the rectangle.</param>
        /// <param name="width">Width of the rectangle.</param>
        /// <param name="height">Height of the rectangle.</param>
        public Rectangle(int x, int y, int width, int height) 
        {
            this.X = x;
            this.Y = y;
            this.Width = width;
            this.Height = height;
        }

        /// <summary>
        /// Compares two rectangles for inequality.
        /// </summary>
        /// <param name="a">Source rectangle.</param>
        /// <param name="b">Source rectangle.</param>
        /// <returns>true if the rectangles are not equal; false otherwise.</returns>
        public static bool operator !=(Rectangle a, Rectangle b)
        {
            return !(a == b);
        }
        
        /// <summary>
        /// Compares two rectangles for equality.
        /// </summary>
        /// <param name="a">Source rectangle.</param>
        /// <param name="b">Source rectangle.</param>
        /// <returns>true if the rectangles are equal; false otherwise.</returns>
        public static bool operator ==(Rectangle a, Rectangle b)
        {
            return (a.X == b.X && 
                    a.Y == b.Y && 
                    a.Width == b.Width && 
                    a.Height == b.Height);
        }
        
        /// <summary>
        /// Returns the y-coordinate of the bottom of the rectangle.
        /// </summary>
        public int Bottom
        {
            get
            {
                return this.Y + this.Height;
            }
        }
        
        /// <summary>
        /// Gets the Point that specifies the center of the rectangle.
        /// </summary>
        public Point Center
        {
            get
            {
                return new Point((this.Right + this.Left) / 2, (this.Bottom + this.Top) / 2);
            }
        }
        
        /// <summary>
        /// Returns a Rectangle with all of its values set to zero.
        /// </summary>
        public static Rectangle Empty
        {
            get
            {
                return new Rectangle(0, 0, 0, 0);
            }
        }
        
        /// <summary>
        /// Returns true if the rectangle is empty, otherwise false.
        /// </summary>
        public bool IsEmpty
        {
            get
            {
                return this == Rectangle.Empty;
            }
        }
        
        /// <summary>
        /// Returns the x-coordinate of the left side of the rectangle.
        /// </summary>
        public int Left
        {
            get
            {
                return this.X;
            }
        }
        
        /// <summary>
        /// Gets or sets the upper-left value of the Rectangle.
        /// </summary>
        public Point Location 
        {
            get
            {
                return new Point(this.X, this.Y);
            }
            set
            {
                this.X = (int)value.X;
                this.Y = (int)value.Y;
            }
        }

        /// <summary>
        /// Returns the x-coordinate of the right side of the rectangle.
        /// </summary>
        public int Right
        {
            get
            {
                return this.X + this.Width;
            }
        }

        /// <summary>
        /// Returns the y-coordinate of the top of the rectangle.
        /// </summary>
        public int Top
        {
            get
            {
                return this.Y;
            }
        }

        /// <summary>
        /// Determines whether this Rectangle contains a specified Point.
        /// </summary>
        /// <param name="value">The Point to evaluate.</param>
        /// <returns>true if the specified Point is contained within this Rectangle; false otherwise.</returns>
        public bool Contains(Point value)
        {
            if (value.X >= this.Left && value.X <= this.Right &&
                value.Y >= this.Top && value.Y <= this.Bottom)
            {
                return true;
            }
            else
                return false;
        }

        /// <summary>
        /// Determines whether this Rectangle entirely contains a specified Rectangle.
        /// </summary>
        /// <param name="value">The Rectangle to evaluate.</param>
        /// <returns>true if this Rectangle entirely contains the specified Rectangle; false otherwise.</returns>
        public bool Contains(Rectangle value)
        {
            return this.Contains(new Point(value.Left, value.Top)) &&
                   this.Contains(new Point(value.Right, value.Top)) &&
                   this.Contains(new Point(value.Left, value.Bottom)) &&
                   this.Contains(new Point(value.Right, value.Bottom));
        }
   
        /// <summary>
        /// Returns a value that indicates whether the current instance is equal to a
        /// specified object.
        /// </summary>
        /// <param name="obj">Object to make the comparison with.</param>
        /// <returns>true if the current instance is equal to the specified object; false otherwise.</returns>
        public override bool Equals(object obj) 
        {
            return this.GetHashCode() == obj.GetHashCode();
        }
 
        /// <summary>
        /// Determines whether the specified System.Object is equal to the Rectangle.
        /// </summary>
        /// <param name="other">The System.Object to compare with the current Rectangle.</param>
        /// <returns>true if the specified System.Object is equal to the current Rectangle; false otherwise.</returns>
        public bool Equals(Rectangle other)
        {
            if (this.IsEmpty && other.IsEmpty)
            {
                return true;
            }

            return this.X.Equals(other.X) && 
                   this.Y.Equals(other.Y);
        }

        /// <summary>
        /// Gets the hash code for this object.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode() 
        {
            return this.X.GetHashCode() ^
                   this.Y.GetHashCode() ^
                   this.Width.GetHashCode() ^
                   this.Height.GetHashCode();
        }

        /// <summary>
        /// Creates a Rectangle defining the area where one rectangle overlaps with another rectangle.
        /// </summary>
        /// <param name="value1">The first Rectangle to compare.</param>
        /// <param name="value2">The second Rectangle to compare.</param>
        /// <returns>The area where the two parameters overlap.</returns>
        public static Rectangle Intersect(Rectangle value1, Rectangle value2)
        {
            int l = 0;
            int t = 0;
            int r = 0;
            int b = 0;

            l = value1.Left > value2.Left ? value1.Left : value2.Left;
            t = value1.Top > value2.Top ? value1.Top : value2.Top;

            if (value1.Right < l || value2.Right < l ||
                value1.Bottom < t || value2.Bottom < t) return Rectangle.Empty;

            r = value1.Right < value2.Right ? value1.Right : value2.Right;
            b = value1.Bottom < value2.Bottom ? value1.Bottom : value2.Bottom;

            return new Rectangle(l, t, r - l, b - t);
        }

        /// <summary>
        /// Creates a Rectangle defining the area where one rectangle overlaps with another rectangle.
        /// </summary>
        /// <param name="value1">The first Rectangle to compare.</param>
        /// <param name="value2">The second Rectangle to compare.</param>
        /// <param name="result">The area where the two first parameters overlap.</param>
        public static void Intersect(ref Rectangle value1, ref Rectangle value2, out Rectangle result)
        {
            result = Rectangle.Intersect(value1, value2);
        }

        /// <summary>
        /// Determines whether a specified Rectangle intersects with this Rectangle.
        /// </summary>
        /// <param name="value">The Rectangle to evaluate.</param>
        /// <returns>true if the specified Rectangle intersects with this one; false otherwise.</returns>
        public bool Intersects(Rectangle value)
        {
            return Rectangle.Intersect(this, value) != Rectangle.Empty;
        }

        /// <summary>
        /// Determines whether a specified Rectangle intersects with this Rectangle.
        /// </summary>
        /// <param name="value">The Rectangle to evaluate</param>
        /// <param name="result">true if the specified Rectangle intersects with this one; false otherwise.</param>
        public void Intersects(ref Rectangle value, out bool result)
        {
            result = this.Intersects(value);
        }

        /// <summary>
        /// Retrieves a string representation of the current object.
        /// </summary>
        /// <returns>String that represents the object.</returns>
        public override string ToString()
        {
            return String.Format("{{X:{0} Y:{1} Width:{2} Height:{3}{4}", this.X, this.Y, this.Width, this.Height, "}");
        }
    }
}
